﻿using System;
using System.Collections.Generic;
using GameFramework.Battle.Core;
using Gameplay.PVE.Utils;
using RootMotion.FinalIK;
using UnityEngine;
using UnityEngine.AI;

namespace Gameplay.PVE.Survivor
{
    public class NavmeshComponent: ComponentBase
    {
        private NavMeshAgent agent;
        private GameObject agentObject;
        private NavMeshObstacle obstacle;
        private GameObject obstacleObject;
        private NavMeshPath path;

        private bool agentValid = false;

        private bool isNavmeshDebug = false;
        private GameObject navmeshTarget;

        private bool isBoss;
        
        protected override void OnInitialized()
        {
            if (path == null)
            {
                path = new NavMeshPath();
            }

            //isNavmeshDebug = unit.unitId == 1001;
            //if (unit.team == 0)
            {
                var cube = new GameObject("Agent " + unit.unitId);//GameObject.CreatePrimitive(PrimitiveType.Cube));
                cube.transform.parent = SurvivorManager.Instance.Root;
                cube.transform.position = unit.Position;
                agentObject = cube;
                agent = cube.AddComponent<NavMeshAgent>();
                agent.speed = unit.moveSpeed;
                agent.acceleration = 999;
                agent.angularSpeed = 240;
                //agent.obstacleAvoidanceType = ObstacleAvoidanceType.NoObstacleAvoidance;
                //agent.avoidancePriority = 0;
                agent.updatePosition = true;
                agent.radius = 0.5f;
                agent.autoBraking = unit.team != 0;
                agent.transform.position = unit.Position;
                agent.agentTypeID = NavMesh.GetSettingsByIndex(2).agentTypeID;
                //agent.areaMask = BattleHelper.GetNavmeshMask(false);
                agentValid = MapManager.Instance.IsMapReady;
                cachedTarget = default;
                isBoss = false;
                var isSelfRvo = unit.team != 0;
                if (unit.team == 2)
                {
                    agentActive = true;
                }
                if (unit.config != null && unit.config.is_boss > 0)
                {
                    isBoss = true;
                    agentActive = false;
                    isSelfRvo = true;
                }

                if (isNavmeshDebug)
                {
                    var cube2 = GameObject.CreatePrimitive(PrimitiveType.Cube);
                    cube2.transform.parent = cube.transform;
                    cube2.transform.localPosition = Vector3.zero;
                }
                SetAgentActive(agentActive,isSelfRvo);
            }
        }

        private Vector3 cachedTarget;
        
        public void InitAgent()
        {
            agentValid = true;
            if (cachedTarget != default)
            {
                agent?.SetDestination(cachedTarget);
                cachedTarget = default;
            }

            if (unit.team == 0)
            {
                unit.Position = BattleHelper.GetNavmeshValidPosition(unit.position);
            }
        }

        public void OnMoveSpeedChanged()
        {
            if (agent != null)
            {
                agent.speed = unit.moveSpeed;
            }
        }
        
        protected override void OnUpdate()
        {
            if (unit.isDead)
            {
                return;
            }
            

            if (!agentActive && agent != null)
            {
                agent.transform.position = unit.Position;
            }
            /*if (agent != null)
            {
                if (unit.TargetPosition != default)
                {
                    agent.SetDestination(unit.TargetPosition);
                }
            }*/
            //UpdateMovement();
            if (obstacle && obstacle.enabled)
            {
                obstacle.transform.position = unit.position;
            }
        }

        private Vector3 velocity;
        private Vector3 agentTargetPosition;

        public void SetAgentStopped(bool stopped)
        {
            if (agentActive && agent != null)
            {
                agent.isStopped = stopped;
            }
        }
        
        public void SetAgentTargetPosition(Vector3 position)
        {
            if (!agentActive)
            {
                agentTargetPosition = position;
                return;
            }
            if (!agentValid)
            {
                cachedTarget = position;
                return;
            }

            var distance = Vector3.Distance(position, unit.Position);
            if (unit.isManualMoving || unit.isInteracting || distance > 0.5f)
            {
                if (agent)
                {
                    if (isNavmeshDebug)
                    {
                        ShowTarget(position);
                    }
                    agent.SetDestination(position);
                    /*if (unit.isManualMoving && distance > 0.5f)
                    {
                        agent.speed = unit.moveSpeed * 1.5f;
                    }
                    else*/
                    {
                        agent.speed = unit.moveSpeed;
                    }
                }
            }
            else
            {
                if (agent)
                {
                    if (isNavmeshDebug)
                    {
                        ShowTarget(agent.transform.position);
                    }
                    agent.SetDestination(agent.transform.position);
                    agent.velocity = Vector3.zero;
                }
            }
        }
        
        private void UpdateMovement()
        {
            if (unit.isInteracting)
            {
                return;
            }
            /*if (unit.TargetPosition == default)
            {
                //agent.SetDestination(agent.transform.position);
                //agent.velocity = Vector3.zero;
                unit.SetSpeed(0);
                return;
            }*/
            var speedThisFrame = unit.moveSpeed * Time.deltaTime;
            velocity.y = 0;
            var diff = velocity;
            var diffLength = diff.magnitude;
            if (diffLength < 0.01f)
            {
                //unit.TargetPosition = default;
                unit.SetSpeed(0);
            }
            else if (diffLength < speedThisFrame)
            {
                unit.SetSpeed(1);
            }
            else
            {
                unit.SetSpeed(1);
            }

            if (unit.isManualMoving)
            {
                if (!agentActive)
                {
                    unit.TargetForward = unit.manualMovingDirection;
                }
                else if (unit.TargetPosition != default)
                {
                    if (velocity != Vector3.zero)
                    {
                        unit.TargetForward = velocity;
                    }
                }
                else
                {
                    unit.TargetForward = unit.manualMovingDirection;
                }
            }
            else if (agentActive)
            {
                if (unit.TargetPosition != default)
                {
                    if (velocity != Vector3.zero && !unit.isInteracting)
                    {
                        if (unit.TargetForward == default || Vector3.Angle(unit.TargetForward, velocity) > 10 || Vector3.Distance(unit.position,unit.TargetPosition) > 5)
                        {
                            unit.TargetForward = agent.transform.forward;
                        }
                    }
                     //unit.TargetPosition - unit.Position;
                }
            }
            //UnityEngine.Debug.LogError(diffLength + " " + diff + " " + agent.desiredVelocity + " " + agent.desiredVelocity.magnitude);
        }

        private float lastLowSpeedTime = -1f;
        private Vector3 lowSpeedStartPosition;
        private bool isCanMove;
        
        public override void LateUpdate()
        {
            if (unit.isDead)
            {
                return;
            }
            base.LateUpdate();
            var isCanMove = unit.CanMove;
            if (isCanMove != this.isCanMove)
            {
                if (agentActive && agent != null)
                {
                    agent.isStopped = !isCanMove;
                    agent.transform.position = unit.position;
                }
                this.isCanMove = isCanMove;
            }
            if (!isCanMove)
            {
                if (agentActive && agent != null)
                {
                    agent.transform.position = unit.position;
                }
                return;
            }
            if (!agentActive)
            {
                if (unit.isInteracting)
                {
                    
                }
                else if (unit.TargetPosition != default)
                {
                    if (isBoss)
                    {
                        var newPos = BattleHelper.MoveByNavMesh(unit.position, unit.TargetPosition, unit.moveSpeed);
                        velocity = newPos - unit.Position;
                        unit.TargetForward = velocity;
                        unit.SetPositionValue(newPos);
                    }
                    else
                    {
                        velocity = unit.TargetPosition - unit.Position;
                        var basicMoveSpeed = unit.moveSpeed * Time.deltaTime;
                        if (velocity.magnitude > basicMoveSpeed)
                        {
                            unit.SetPositionValue(unit.Position + basicMoveSpeed * velocity.normalized);
                        }
                        else
                        {
                            unit.SetPositionValue(unit.TargetPosition);
                        }
                    }
                    unit.Transform.position = unit.Position;
                }
                else
                {
                    velocity = Vector3.zero;
                }
            }
            else if (agentObject)
            {
                var position = agentObject.transform.position;
                velocity = position - unit.Position;
                velocity.y = 0;
                var agentOriginPosition = position;
                var basicMoveSpeed = unit.moveSpeed * Time.deltaTime;
                var hasFixed = false;
                if (unit.isManualMoving && false)// && velocity.magnitude == 0)
                {
                    if (agent.CalculatePath(unit.TargetPosition, path))
                    {
                        //if (IsPathValid(path, unit.Position, unit.TargetPosition))
                        {
                            var isSuccess = BattleHelper.GetNavmeshPathPoint(path, basicMoveSpeed,ref position);
                            if (isSuccess)
                            {
                                agent.Warp(position);
                                agent.transform.position = position;
                                velocity = position - unit.Position;
                                velocity.y = 0;
                                hasFixed = true;
                            }
                        }
                        /*else
                        {
                            agent.FindClosestEdge(out var hit);
                            agent.SetDestination(hit.position);
                            velocity = (hit.position - unit.Position).normalized * basicMoveSpeed;
                        }*/
                       
                    }
                    else
                    {
                        //BattleDebug.LogError("CalculatePath Failed " + unit.unitId);
                    }
                }
                var velocitySqr = velocity.sqrMagnitude;
                
                if (velocitySqr > basicMoveSpeed * basicMoveSpeed)
                {
                    velocity = velocity.normalized * basicMoveSpeed;
                    position = unit.Position + velocity;
                }

                /*if (velocitySqr < basicMoveSpeed * basicMoveSpeed / 4)
                {
                    //低速模式，需要进行卡位预警
                    if (lastLowSpeedTime < 0)
                    {
                        lastLowSpeedTime = TimeManager.time;
                        lowSpeedStartPosition = position;
                    }
                }
                else
                {
                    lastLowSpeedTime = -1;
                }

                if (lastLowSpeedTime > 0 && TimeManager.time - lastLowSpeedTime > 0.5f)
                {
                    if (unit.TargetPosition != default && unit.TargetPosition != position && Vector3.Distance(lowSpeedStartPosition, position) < 0.3f)
                    {
                        position = unit.position;
                        BattleDebug.LogError(unit.unitId + "卡住");
                        velocity = Vector3.zero;
                        agent.transform.position = position;
                        unit.TargetForward = unit.TargetPosition - position;
                    }
                    else
                    {
                        lastLowSpeedTime = -1;
                    }
                }*/
                
                unit.SetPositionValue(position);
                unit.Transform.position = position;
            }
            UpdateMovement();
            //UnityEngine.Debug.LogError(velocity.magnitude);
        }

        private bool IsPathValid(NavMeshPath path,Vector3 startPosition,Vector3 targetPosition)
        {
            //计算原始距离
            var originDistance = targetPosition - startPosition;
            var distanceSqr = originDistance.sqrMagnitude;
            if (distanceSqr < 1)
            {
                return true;
            }
            var distance = Mathf.Sqrt(distanceSqr);
            //如果移动距离为原始距离的1.5倍以上，则认定为无效路径
            if (path.corners.Length > 1)
            {
                var pathDistance = 0f;
                for (var i = 0; i < path.corners.Length - 1; i++)
                {
                    pathDistance += Vector3.Distance(path.corners[i], path.corners[i + 1]);
                }
                if (pathDistance > distance * 1.5f)
                {
                    return false;
                }
            }
            return true;
        }

        public void SetPosition(Vector3 position)
        {
            if (agentObject)
            {
                agent.Warp(position);
                agentObject.transform.position = position;
            }
        }

        private void ShowTarget(Vector3 position)
        {
            if (navmeshTarget == null)
            {
                navmeshTarget = GameObject.CreatePrimitive(PrimitiveType.Sphere);
                navmeshTarget.transform.parent = unit.Transform;
                navmeshTarget.transform.localScale = new Vector3(0.3f, 0.3f, 0.3f);
            }
            navmeshTarget.transform.position = position;
        }

        protected override void OnDead()
        {
            base.OnDead();
            RemoveAgent();
        }

        private bool agentActive = true;

        public void SetAgentActive(bool active,bool selfRVO = false)
        {
            agentActive = active;
            if (agent != null)
            {
                //agent.enabled = active || selfRVO;
                agent.enabled = active;
                agent.updatePosition = active;
                if (active)
                {
                    DisableObstacle();
                    agent.Warp(unit.Position);
                }
                else
                {
                    if (selfRVO)
                    {
                        EnableObstacle(false);
                    }
                }
            }
        }
        
        public void SetAgentValid(bool valid)
        {
            agentValid = valid;
        }

        public bool IsAgentActive()
        {
            return agentActive;
        }

        private void RemoveAgent()
        {
            if (agent != null)
            {
                GameObject.DestroyImmediate(agent.gameObject);
                agentObject = null;
                agent = null;
            }
            path.ClearCorners();
        }

        public void EnableObstacle(bool isCarving)
        {
            if (obstacleObject == null)
            {
                var cube = new GameObject("Obstacle " + unit.unitId);
                cube.transform.parent = SurvivorManager.Instance.Root;
                cube.transform.position = unit.Position;
                obstacleObject = cube;
                obstacle = cube.AddComponent<NavMeshObstacle>();
            }
            obstacle.enabled = true;
            obstacle.carving = isCarving;
            obstacle.carveOnlyStationary = true;
            obstacle.carvingTimeToStationary = 0.1f;
            obstacle.shape = NavMeshObstacleShape.Capsule;
            obstacle.size = new Vector3(0.3f, 2, 0);
            /*if (unit.team == 0)
            {
                obstacle.a
            }*/
            if (isCarving)
            {
                obstacle.transform.position = unit.TargetPosition;
            }
            else
            {
                obstacle.transform.position = unit.Position;
            }
        }

        public void DisableObstacle()
        {
            if (obstacle)
            {
                obstacle.enabled = false;
            }
        }
        
        private void RemoveObstacle()
        {
            if (obstacle != null)
            {
                GameObject.DestroyImmediate(obstacle.gameObject);
                obstacleObject = null;
                obstacle = null;
            }
        }

        
        public void OnPause()
        {
            if (agent && agentActive)
            {
                agent.isStopped = true;
            }
        }

        public void OnResume()
        {
            if (agent && agentActive)
            {
                agent.isStopped = false;
            }
        }
        
        public override void OnReset()
        {
            base.OnReset();
            RemoveAgent();
            RemoveObstacle();
            agentActive = true;
            cachedTarget = default;
        }
    }
}
